#include "udpserver.hpp"
#include <memory>
#include <unordered_map>
#include <fstream>
#include <cstring>

string filename("./dict.txt");                            // 字典文件名
unordered_map<string, string> dict;                       // 字典
unordered_map<string, pair<string, uint16_t>> onlineUser; // 在线用户信息

// 服务器使用手册
void usage(const string &procname)
{
    cerr << "Usage:\n\t";
    cerr << procname << " serverPort" << endl;
}

// 打印字典
void printDict()
{
    for (auto &kv : dict)
        cout << kv.first << " : " << kv.second << endl;
}

// 拆分词组
void Splitline(string &line, string &key, string &value, char sign)
{
    auto pos = line.find(sign);
    if (pos != string::npos)
    {
        key = line.substr(0, pos);
        value = line.substr(pos + 1);
    }
}

// 加载字典
void Load_dictionary()
{
    ifstream rd(filename); // 从文件filename中读取数据
    string line;
    string key;
    string value;
    while (getline(rd, line))
    {
        Splitline(line, key, value, ':');
        dict[key] = value;
    }
}

// 将翻译的结果发送给客户端
void translate(int sockfd, const string &clientIp, uint16_t clientPort, const string &message)
{
    struct sockaddr_in clientinfor;
    clientinfor.sin_family = AF_INET;
    clientinfor.sin_addr.s_addr = inet_addr(clientIp.c_str());
    clientinfor.sin_port = htons(clientPort);

    string result;
    if (dict.find(message) != dict.end()) // 存在对应的单词记录
        result = dict[message] + '\n';
    else
        result = "unknow\n";

    sendto(sockfd, result.c_str(), result.size(), 0, (struct sockaddr *)&clientinfor, sizeof(clientinfor));
}

// 执行命令行操作，再将输出结果发送给客户端
void execCommand(int sockfd, const string &clientIp, uint16_t clientPort, const string &command)
{
    struct sockaddr_in clientinfor;
    clientinfor.sin_family = AF_INET;
    clientinfor.sin_addr.s_addr = inet_addr(clientIp.c_str());
    clientinfor.sin_port = htons(clientPort);

    FILE *pf = popen(command.c_str(), "r");
    string respond; // 指令执行结果
    if (pf == NULL)
        respond = command + " exec failed";
    else
    {
        char buffer[BUFSIZE];
        while (fgets(buffer, sizeof(buffer), pf))
            respond += buffer;
    }

    sendto(sockfd, respond.c_str(), respond.size(), 0, (struct sockaddr *)&clientinfor, sizeof(clientinfor));

    fclose(pf);
    pf = nullptr;
}

// 将受到的消息转发所有在线用户
void forwardMessage(int sockfd, const string &clientIp, uint16_t clientPort, const string &message)
{
    // 1、构建用户信息
    struct sockaddr_in clientinfor;
    clientinfor.sin_family = AF_INET;
    clientinfor.sin_addr.s_addr = inet_addr(clientIp.c_str());
    clientinfor.sin_port = htons(clientPort);
    string key = clientIp + "[" + to_string(clientPort) + "]# ";
    pair<string, uint16_t> value(clientIp, clientPort);

    string respond; // 往回发送的信息
    // 2、查看是否登录操作，如果是，则只需要给登录用户单独回复信息
    if (message == "online")
    {
        if (onlineUser.find(key) != onlineUser.end()) // 已经在线
            respond = "您已在线，请勿重复登陆";
        else // 不在线
        {
            respond = "登陆成功";
            onlineUser[key] = value; // 将用户添加至在线列表
        }

        sendto(sockfd, respond.c_str(), respond.size(), 0, (struct sockaddr *)&clientinfor, sizeof(clientinfor));

        return;
    }

    // 3、非登录请求。先查看用户是否在线，不在线则单独给用户发送信息；
    if (onlineUser.find(key) == onlineUser.end()) // 用户不在线
    {
        respond = "当前处于离线状态，请输入：online 进行登录";
        sendto(sockfd, respond.c_str(), respond.size(), 0, (struct sockaddr *)&clientinfor, sizeof(clientinfor));

        return;
    }

    // 4、非登录请求且用户在线，将其信息转发全部在线用户
    respond = key + message;
    for (auto &kv : onlineUser)
    {
        // 获取要发送的客户用户信息
        struct sockaddr_in client;
        client.sin_family = AF_INET;
        client.sin_addr.s_addr = inet_addr(kv.second.first.c_str());
        client.sin_port = htons(kv.second.second);
        sendto(sockfd, respond.c_str(), respond.size(), 0, (struct sockaddr *)&client, sizeof(client));
    }
}

//./udpserver port
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        usage(argv[0]);
        exit(1);
    }

    // Load_dictionary(); // 加载字典
    //  printDict;       // 打印字典

    // 创建服务器
    // unique_ptr<udpserver> serverPtr(new udpserver(stoi(argv[1]), translate)); // 场景一：词典翻译
    // unique_ptr<udpserver> serverPtr(new udpserver(stoi(argv[1]), execCommand)); // 场景二：指令操作
    unique_ptr<udpserver> serverPtr(new udpserver(stoi(argv[1]), forwardMessage)); // 场景三：聊天室
    serverPtr->init();
    serverPtr->start();

    return 0;
}